﻿/*Copyright by Zak - http://www.xnamag.de/forum/viewtopic.php?t=1062 */


using System;
using System.Security;
using System.Runtime.InteropServices;


namespace MECopter.Joystick
{
    public static class Joystick
    {

        //----------------------------------------------------------------- 
        // Fehlercodes 
        //----------------------------------------------------------------- 
        private const uint JOYERR_BASE = 160;
        private const uint MMSYSERR_BASE = 0;
        private const uint ERR_NOERROR = 0;
        private const uint ERR_BADDEVICEID = (MMSYSERR_BASE + 2);
        private const uint ERR_PARMS = (JOYERR_BASE + 5);
        private const uint ERR_NOCANDO = (JOYERR_BASE + 6);
        private const uint ERR_UNPLUGGED = (JOYERR_BASE + 7);
        private const uint ERR_NODRIVER = (MMSYSERR_BASE + 6);
        private const uint ERR_INVALPARAM = (MMSYSERR_BASE + 11);

        //----------------------------------------------------------------- 
        // Flags 
        //----------------------------------------------------------------- 
        private const uint JOY_RETURNALL = 255;

        internal static MECopter.Quadrocopter.ManualController ManualController
        {
            get
            {
                throw new System.NotImplementedException();
            }
            set
            {
            }
        }

        //----------------------------------------------------------------- 
        // JOYCAPSW Struktur für Unicode. 
        // Enthält Joystick/ Treibereigenschaften. 
        //----------------------------------------------------------------- 
        [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
        private class JOYCAPSW
        {
            public ushort wMid;
            public ushort wPid;
            [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)]
            public string szPname;
            public uint wXmin;
            public uint wXmax;
            public uint wYmin;
            public uint wYmax;
            public uint wZmin;
            public uint wZmax;
            public uint wNumButtons;
            public uint wPeriodMin;
            public uint wPeriodMax;
            public uint wRmin;
            public uint wRmax;
            public uint wUmin;
            public uint wUmax;
            public uint wVmin;
            public uint wVmax;
            public uint wCaps;
            public uint wMaxAxes;
            public uint wNumAxes;
            public uint wMaxButtons;
            [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)]
            public string szRegKey;
            [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 260)]
            public string szOEMVxD;
        }

        //----------------------------------------------------------------- 
        // JOYINFOEX Struktur. 
        // Enthält Achsen, Position und Tastenstatus. 
        //----------------------------------------------------------------- 
        [StructLayout(LayoutKind.Sequential)]
        private class JOYINFOEX
        {
            public uint dwSize;
            public uint dwFlags;
            public uint dwXpos;
            public uint dwYpos;
            public uint dwZpos;
            public uint dwRpos;
            public uint dwUpos;
            public uint dwVpos;
            public uint dwButton;
            public uint dwButtonNumber;
            public uint dwPOV;
            public uint dwReserved1;
            public uint dwReserved2;
        }

        //----------------------------------------------------------------- 
        // Funktion für die Abfrage der 
        // Joystick/ Treibereigenschaften. 
        //----------------------------------------------------------------- 
        [DllImport("winmm.dll",
          CallingConvention =
          CallingConvention.StdCall,
          SetLastError = true,
          CharSet = CharSet.Ansi),
          SuppressUnmanagedCodeSecurity]
        private static extern uint joyGetDevCapsW(uint id, [In, Out] JOYCAPSW lpCaps, uint uSize);

        //----------------------------------------------------------------- 
        // Funktion für die Abfrage 
        // von Achsen, Position und Tastenstatus. 
        //----------------------------------------------------------------- 
        [DllImport("winmm.dll",
          CallingConvention = CallingConvention.StdCall,
          SetLastError = true,
          CharSet = CharSet.Ansi),
          SuppressUnmanagedCodeSecurity]
        private static extern uint joyGetPosEx(uint uJoyID, [In, Out] JOYINFOEX pji);

        //----------------------------------------------------------------- 
        // Joystick Eigenschaften 
        //----------------------------------------------------------------- 
        public static JoystickCapabilities GetJoystickCapabilities(JoystickIndex joystickIndex)
        {
            JoystickCapabilities joyCaps = new JoystickCapabilities();
            JOYCAPSW joyCapsNestedClass = new JOYCAPSW();

            uint errorCode = joyGetDevCapsW((uint)joystickIndex,
              joyCapsNestedClass,
              (uint)Marshal.SizeOf(joyCapsNestedClass));

            if (errorCode == ERR_NODRIVER)
                throw new Exception("MMSYSERR_NODRIVER");

            if (errorCode == ERR_INVALPARAM)
                throw new Exception("MMSYSERR_INVALPARAM");

            joyCaps.wNumAxis = joyCapsNestedClass.wNumAxes;
            joyCaps.wNumButtons = joyCapsNestedClass.wNumButtons;
            joyCaps.wRmax = joyCapsNestedClass.wRmax;
            joyCaps.wRmin = joyCapsNestedClass.wRmin;
            joyCaps.wUmax = joyCapsNestedClass.wUmax;
            joyCaps.wUmin = joyCapsNestedClass.wUmin;
            joyCaps.wVmax = joyCapsNestedClass.wVmax;
            joyCaps.wVmin = joyCapsNestedClass.wVmin;
            joyCaps.wXmax = joyCapsNestedClass.wXmax;
            joyCaps.wXmin = joyCapsNestedClass.wXmin;
            joyCaps.wYmax = joyCapsNestedClass.wYmax;
            joyCaps.wYmin = joyCapsNestedClass.wYmin;
            joyCaps.wZmax = joyCapsNestedClass.wZmax;
            joyCaps.wZmin = joyCapsNestedClass.wZmin;

            return (joyCaps);
        }

        //----------------------------------------------------------------- 
        // Joystick Status 
        //----------------------------------------------------------------- 
        public static JoystickState GetState(JoystickIndex joystickIndex)
        {
            JoystickState joyState = new JoystickState();
            JOYINFOEX joyInfoExNestedClass = new JOYINFOEX();

            joyInfoExNestedClass.dwSize = (uint)Marshal.SizeOf(joyInfoExNestedClass);
            joyInfoExNestedClass.dwFlags = JOY_RETURNALL;

            uint errorCode = joyGetPosEx((uint)joystickIndex, joyInfoExNestedClass);

            //Fehlertest 
            if (errorCode == ERR_BADDEVICEID)
                throw new Exception("MMSYSERR_BADDEVICEID");

            if (errorCode == ERR_NODRIVER)
                throw new Exception("MMSYSERR_NODRIVER");

            if (errorCode == ERR_INVALPARAM)
                throw new Exception("MMSYSERR_INVALPARAM");

            //Joystick ist nicht verbunden (fixed) 
            if (errorCode == ERR_UNPLUGGED || errorCode == ERR_PARMS)
                joyState.bConnected = false;
            else
                joyState.bConnected = true;

            joyState.dwButton = joyInfoExNestedClass.dwButton;
            joyState.dwButtonNumber = joyInfoExNestedClass.dwButtonNumber;
            joyState.dwPOV = joyInfoExNestedClass.dwPOV;
            joyState.dwRpos = joyInfoExNestedClass.dwRpos;
            joyState.dwUpos = joyInfoExNestedClass.dwUpos;
            joyState.dwVpos = joyInfoExNestedClass.dwVpos;
            joyState.dwXpos = joyInfoExNestedClass.dwXpos;
            joyState.dwYpos = joyInfoExNestedClass.dwYpos;
            joyState.dwZpos = joyInfoExNestedClass.dwZpos;

            return (joyState);
        }
    }
}